//bange_box.cpp
//Copyright (C) 2010-2011 Oscar (.teri) Triano

//This program is free software: you can redistribute it and/or modify
//it under the terms of the GNU General Public License as published by
//the Free Software Foundation, either version 3 of the License, or
//(at your option) any later version.

//This program is distributed in the hope that it will be useful,
//but WITHOUT ANY WARRANTY; without even the implied warranty of
//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//GNU General Public License for more details.

//You should have received a copy of the GNU General Public License
//along with this program.  If not, see <http://www.gnu.org/licenses/>.


#include <bange_box.hpp>
#include <bange_scene.hpp>
#include <bange_view.hpp>
#include <bange_layer.hpp>

bange::box::box(void): vm(NULL), error(false), bgcolor(0, 0, 0, 255){
	this->callexitfunction = LUA_REFNIL;
	this->dataexitfunction = LUA_REFNIL;
	this->exitkey = sf::Key::Escape;
	this->consolekey = sf::Key::F1;
	this->consoleenabled = false;
	this->vm = luaL_newstate();
	this->renderwindow = dynamic_cast<sf::RenderWindow *>(this);
	if (this->vm == NULL){
		cout << "bange: bange without lua is nothing... byebye" << endl;
		this->error = true;
		return;}
}

bool bange::box::Error(void){
	return this->error;
}

bool bange::box::InitFromConsole(const char *game){
	if(!PHYSFS_mount(game, NULL, 0)){
		cout << "bange: " << game << ": " << PHYSFS_getLastError() << endl;
		return false;}
	if (!PHYSFS_exists("run.lua")){
		cout << "bange: The location \"" << game << "\" doesn't have a \"run.lua\" file." << endl;
		return false;}
	if(!this->Configure()){
		return false;}
	/*char **paths = PHYSFS_getSearchPath();
	for (int i = 0; paths[i] != NULL; i+=1){
		cout << paths[i] << " is the search path." << endl;}
	PHYSFS_freeList(paths);*/
     //printf("[%s] is in the search path.\n", *i);
    //Do config
	return true;
}

bool bange::box::InitFromDesktop(void){
	const char *bangefile = "bange.lua";
	char game[255] = {0};
	if(luaL_dofile(vm, bangefile)){
        luaL_error(vm, "\nError in config file \"%s\": %s", bangefile, lua_tostring(vm, -1));
        return false;}
    lua_getglobal(vm, "game");
    if (!lua_isstring(vm, -1)){
		cout << "bange: You must say which directory or package is the game with \"game\" inside \"bange.lua\"." << endl;
		return false;}
	strncpy(game, lua_tostring(vm, -1), 255);
	lua_pop(vm, 1);
	if(!PHYSFS_mount(game, NULL, 0)){
		cout << "bange: " << PHYSFS_getLastError() << endl;
		return false;}
	if (!PHYSFS_exists("run.lua")){
		cout << "bange: The location \"" << game << "\" doesn't have a \"run.lua\" file." << endl;
		return false;}
	if(!this->Configure()){
		return false;}
	/*char **paths = PHYSFS_getSearchPath();
	for (int i = 0; paths[i] != NULL; i+=1){
		cout << paths[i] << " is the search path." << endl;}
	PHYSFS_freeList(paths);*/
     //printf("[%s] is in the search path.\n", *i);
    //Do config
	return true;
}

bool bange::box::Configure(void){
	lua_State *vm = this->vm;
	luaL_openlibs(vm);
	bange::vm::PrepareVM(vm);
	if(PHYSFS_exists("config.lua") != 0){
		char *config = NULL;
		PHYSFS_sint64 readed = bange::readfilefrompackage("config.lua", &config);
		if (readed < 0){
			delete config;
			return false;
		}
		if(luaL_dostring(vm, config)){
			luaL_error(vm, "\nbange: Error in config.lua: %s", lua_tostring(vm, -1));
			delete config;
			return false;}
		delete config;
	}
	sf::VideoMode videomode(640, 480);
	sf::WindowSettings windowsettings;
	unsigned long style = sf::Style::Close;
	char title[255] = "BAN Game Engine";
	//cout << windowsettings.DepthBits << " " << windowsettings.StencilBits << endl;
	int value = 0, fps = 60;
	if (bange::vm::GetInt(vm, "Width", &value)){
        videomode.Width = static_cast<unsigned int>(value);}
    if (bange::vm::GetInt(vm, "Height", &value)){
        videomode.Height = static_cast<unsigned int>(value);}
    if (bange::vm::GetInt(vm, "BitsPerPixel", &value)){
        videomode.BitsPerPixel = static_cast<unsigned int>(value);}
    if (bange::vm::GetInt(vm, "DepthBits", &value)){
        windowsettings.DepthBits = static_cast<unsigned int>(value);}
    if (bange::vm::GetInt(vm, "StencilBits", &value)){
        windowsettings.StencilBits = static_cast<unsigned int>(value);}
    if (bange::vm::GetInt(vm, "AntialiasingLevel", &value)){
        windowsettings.AntialiasingLevel = static_cast<unsigned int>(value);}
    if (bange::vm::GetInt(vm, "ExitKey", &value)){
        this->exitkey = static_cast<sf::Key::Code>(value);}
    if (bange::vm::GetBoolean(vm, "Fullscreen", &value) && value == 1){
        style |= sf::Style::Fullscreen;}
    if (bange::vm::GetBoolean(vm, "Resize", &value) && value == 1){
        style |= sf::Style::Resize;}
    if (bange::vm::GetBoolean(vm, "Close", &value) && value == 0){
        style ^= sf::Style::Close;}
    if (bange::vm::GetBoolean(vm, "Titlebar", &value) && value == 1){
        style |= sf::Style::Titlebar;}
    bange::vm::GetString(vm, "Title", title);
    bange::vm::GetInt(vm, "FPS", &fps);
    //By default VSync will be disabled
    if (bange::vm::GetBoolean(vm, "VSync", &value)){
        this->UseVerticalSync(static_cast<bool>(value));}
    else{
        this->UseVerticalSync(false);}
    //By default ShowCursor is enabled
    if (bange::vm::GetBoolean(vm, "ShowCursor", &value)){
        this->ShowMouseCursor(static_cast<bool>(value));}
    else{
        this->ShowMouseCursor(true);}
    //By default KeyRepeat is disabled
    if (bange::vm::GetBoolean(vm, "KeyRepeat", &value)){
        this->EnableKeyRepeat(static_cast<bool>(value));}
    else{
        this->EnableKeyRepeat(false);}
    this->SetFramerateLimit(fps);
	this->Create(videomode, title, style, windowsettings);
	lua_pushlightuserdata(vm, static_cast<void *>(this));
	lua_setfield(vm, LUA_REGISTRYINDEX, "bangebox");
	return true;
}

void bange::box::ManageEventForConsole(sf::Event &event){
	static bool aberrantkey = false;
	if (event.Type == sf::Event::KeyPressed){
		if (event.Key.Code == sf::Key::Up){
			console.MoveCursorThroughEntries(-1);
			aberrantkey = false;}
		else if (event.Key.Code == sf::Key::Down){
			console.MoveCursorThroughEntries(1);
			aberrantkey = false;}
		else if (event.Key.Code == sf::Key::Left){
			console.MoveCursor(-1);
			aberrantkey = false;}
		else if (event.Key.Code == sf::Key::Right){
			console.MoveCursor(1);
			aberrantkey = false;}
		else if (event.Key.Code == sf::Key::Back){
			console.RemoveChar();
			aberrantkey = true;}
		else if (event.Key.Code == sf::Key::Return){
			const char *chunk = console.GetString();
			std::cout << chunk << std::endl;
			if (chunk == NULL){
				return;}
			int currenttop = lua_gettop(vm);
			if (luaL_dostring(vm, chunk)){
				int nerrors = lua_gettop(vm) - currenttop;
				for (int i = 0; i < nerrors; i += 1){
					if (lua_isnil(vm, -1)){
						lua_pop(vm, 1);
						lua_pushstring(vm, "nil");
					}
					console.AddEntry(lua_tostring(vm, -1));
					lua_pop(vm, 1);
				}
				console.AddEntry("");
			}
			else{
				int nresults = lua_gettop(vm) - currenttop;
				for (int i = 0; i < nresults; i += 1){
					if (lua_isnil(vm, -1)){
						lua_pop(vm, 1);
						lua_pushstring(vm, "nil");
					}
					console.AddEntry(lua_tostring(vm, -1));
					lua_pop(vm, 1);
				}
				console.AddEntry("");
			}
			aberrantkey = true;}
		else{
			aberrantkey = false;}
	}
	else if (event.Type == sf::Event::TextEntered && aberrantkey != true){
		console.AddChar(event.Text.Unicode);
		aberrantkey = false;
	}
	/*
	else if(event.Type == sf::Event::TextEntered){
		cout << "Entry text->icursor: " << console.icursor << endl;	
		sf::Uint32 character = event.Text.Unicode;
		sf::Unicode::UTF32String strentry = entry.GetText();
		cout << "Size: " << strentry.size() << endl;
		strentry.insert(console.icursor, 1, character);
		console.icursor += 1;
		pos = entry.GetCharacterPos(this->console.icursor);
		console.cursor.SetPosition(8+pos.x, pos.y+(this->GetHeight()-console.size));
		entry.SetText(strentry);
	}
	*/
}

void bange::box::DrawConsole(){
	if (this->consoleenabled == false){
		return;}
	
	this->SetView(this->GetDefaultView());
	console.Draw(*static_cast<sf::RenderWindow *>(this));
}

void bange::box::Run(void){
	lua_State *vm = this->vm;
	char *run = NULL;
	PHYSFS_sint64 readed = bange::readfilefrompackage("run.lua", &run);
	cout << "Readed: " << readed << endl;
	cout << "Len runfile: " << strlen(run) << endl;
	if(luaL_dostring(vm, run)){
		luaL_error(vm, "\nbange: Error in \"run.lua\": %s", lua_tostring(vm, -1));
		delete run;
		return;}
	delete run;
    int Run = 1;
    sf::Event event;
    lua_getglobal(vm, "bange");
    sf::Color black(0, 0, 0);
    while (this->IsOpened()){
        lua_getfield(vm, -1, "Exit");
        if (lua_isboolean(vm, -1) && lua_toboolean(vm, -1) == 1){
            this->CallExitFunction();
            this->Close();}
        lua_pop(vm, 1);//Pop boolean/nil value
        lua_getfield(vm, -1, "Run");
        if (!lua_istable(vm, -1)){
            Run = 0;
            lua_pop(vm, 1);}
        else{
            Run = 1;}
        while (this->GetEvent(event)){
             if (event.Type == sf::Event::Closed){
                 this->CallExitFunction();
                 this->Close();}
             else if (event.Type == sf::Event::KeyPressed && event.Key.Control == true \
             && event.Key.Code == this->exitkey){
                 this->CallExitFunction();
                 this->Close();}
             else if (event.Type == sf::Event::KeyPressed && event.Key.Control == true \
             && event.Key.Code == this->consolekey){
				 this->consoleenabled = !this->consoleenabled;}
             else if (Run && lua_istable(vm, -1) && consoleenabled == false){
				this->ManageEvent(event);}
			 else if (consoleenabled){
				this->ManageEventForConsole(event);}
			}		
        this->Clear(this->bgcolor);
        if(Run){
            this->ProcessScenes();
            this->ProcessViews();}
        this->DrawConsole();
        this->Display();
        if (Run){
            lua_pop(vm, 1);}
    }
    cout << "\nExit lua top: " << lua_gettop(vm) << endl;
}

void bange::box::ManageEvent(sf::Event &event){
	using namespace sf;
	int indexview = 0, indexscene = 0, indexlayer = 0, \
	indexevent = 0, refparent = LUA_REFNIL;
	bool createeventdataforview = false;
	bange::baseproxy *proxy = NULL;
	bange::view *view = NULL;
	bange::scene *scene = NULL;
	bange::layer *layer = NULL;
	if (event.Type == Event::MouseButtonPressed\
		 || event.Type == Event::MouseButtonReleased\
		 || event.Type == Event::MouseMoved){
		
		createeventdataforview = true;
		
	}
	else{
		this->CreateEventData(event);
		indexevent = lua_gettop(vm);
	}
	//Index 2 is the table with the views and scenes
	lua_getfield(vm, 2, "views");
	if (!lua_istable(vm, -1)){
		lua_pop(vm, 1);
		if (createeventdataforview == false){
			lua_pop(vm, 1);//Event data
		}
		return;
	}
	lua_pushnil(vm);
	while(lua_next(vm, -2)){
		view = dynamic_cast<bange::view *>(bange::base::GetBaseFromProxy(-1, vm));
		if (view == NULL){
			cout << "bange: Isn't possible manage a event for a object that isn't a view!" << endl;
			//Next
			lua_pop(vm, 1);
			continue;
		}
		indexview = lua_gettop(vm);
		if (createeventdataforview == true){
			this->CreateEventDataForView(event, view->theview);
			indexevent = lua_gettop(vm);
		}
		view->ExecuteEvents(indexview, indexevent, event, vm);
		if (view->refscene != LUA_REFNIL){
			lua_rawgeti(vm, LUA_REGISTRYINDEX, view->refscene);
			scene = static_cast<bange::scene *>(bange::base::GetBaseFromProxy(-1, vm));
			indexscene = lua_gettop(vm);
			scene->ExecuteEvents(indexscene, indexevent, event, vm);
			lua_pushvalue(vm, indexscene);
			refparent = luaL_ref(vm, LUA_REGISTRYINDEX);
			for(int i = 0; i < scene->nlayers; i += 1){
				if (scene->layers[i] == NULL){
					continue;}
				bange::BuildProxy(refparent, static_cast<bange::base *>(scene->layers[i]), vm);
				indexlayer = lua_gettop(vm);
				scene->layers[i]->ExecuteEvents(indexlayer, indexevent, event, vm);
				scene->layers[i]->ManageEvent(indexlayer, indexevent, event, vm);
				lua_pop(vm, 1);//Layer proxy
			}
			lua_pop(vm, 1);//scene
		}
		if (createeventdataforview == true){
			lua_pop(vm, 1);//Event data for view
		}
		//Next
		lua_pop(vm, 1);
	}
	lua_pop(vm, 1);//Views
	if (createeventdataforview == false){
		lua_pop(vm, 1);//Event data
	}
}

void bange::box::CreateEventData(sf::Event &event){
	sf::Vector2f mouse;
	lua_State *vm = this->vm;
	using namespace sf;
	switch (event.Type){
        case Event::KeyPressed: case Event::KeyReleased:
            lua_createtable(vm, 0, 4);
            lua_pushnumber(vm, event.Key.Code);
            lua_setfield(vm, -2, "Code");
            lua_pushboolean(vm, event.Key.Alt);
            lua_setfield(vm, -2, "Alt");
            lua_pushboolean(vm, event.Key.Control);
            lua_setfield(vm, -2, "Control");
            lua_pushboolean(vm, event.Key.Shift);
            lua_setfield(vm, -2, "Shift");
        break;
        case Event::MouseWheelMoved:
			//Only a number and not a table
            lua_pushnumber(vm, event.MouseWheel.Delta);
        break;
        case Event::JoyButtonPressed: case Event::JoyButtonReleased:
            lua_createtable(vm, 0, 2);
            lua_pushnumber(vm, event.JoyButton.JoystickId);
            lua_setfield(vm, -2, "Id");
            lua_pushnumber(vm, event.JoyButton.Button);
            lua_setfield(vm, -2, "Button");
        break;
        case Event::JoyMoved:
            lua_createtable(vm, 0, 3);
            lua_pushnumber(vm, event.JoyMove.JoystickId);
            lua_setfield(vm, -2, "Id");
            lua_pushnumber(vm, event.JoyMove.Axis);
            lua_setfield(vm, -2, "Axis");
            lua_pushnumber(vm, event.JoyMove.Position);
            lua_setfield(vm, -2, "Position");
        break;
        case Event::TextEntered:
			//Only a number and not a table
            lua_pushnumber(vm, event.Text.Unicode);
        break;
        case Event::LostFocus: case Event::GainedFocus: default:
			//Only nil
            lua_pushnil(vm);
        break;
    }
}

void bange::box::CreateEventDataForView(sf::Event &event, sf::View *view){
	sf::Vector2f mouse;
	lua_State *vm = this->vm;
	using namespace sf;
	switch(event.Type){
	        case Event::MouseButtonPressed: case Event::MouseButtonReleased:
            lua_createtable(vm, 0, 3);
            lua_pushnumber(vm, event.MouseButton.Button);
            lua_setfield(vm, -2, "Button");
            mouse = this->ConvertCoords(event.MouseButton.X, event.MouseButton.Y, view);
            lua_pushnumber(vm, mouse.x);
            lua_setfield(vm, -2, "X");
            lua_pushnumber(vm, mouse.y);
            lua_setfield(vm, -2, "Y");
        break;
        case Event::MouseMoved:
            lua_createtable(vm, 0, 2);
            mouse = this->ConvertCoords(event.MouseMove.X, event.MouseMove.Y, view);
            lua_pushnumber(vm, mouse.x);
            lua_setfield(vm, -2, "X");
            lua_pushnumber(vm, mouse.y);
            lua_setfield(vm, -2, "Y");
        break;
	}
}

void bange::box::ProcessScenes(void){
	int indexscene = 0, indexlayer = 0, refscene = LUA_REFNIL;
	bange::baseproxy *proxy;
	bange::scene *scene = NULL;
	bange::layer *layer = NULL;
	lua_getfield(vm, 2, "scenes");
	if (!lua_istable(vm, -1)){
		lua_pop(vm, 1);
		return;}
	float frametime = this->GetFrameTime();
	lua_pushnil(this->vm);
	while(lua_next(this->vm, -2)){
		//Continue
		//proxy = static_cast<bange::baseproxy *>(lua_touserdata(this->vm, -1));
		//scene = dynamic_cast<bange::scene *>(proxy->base);
		bange::scene *scene = dynamic_cast<bange::scene *>(bange::base::GetBaseFromProxy(-1, vm));
		if (scene == NULL){
			cout << "bange: object to be processed isn't a scene!" << endl;
			lua_pop(this->vm, 1);
			continue;}
		indexscene = lua_gettop(vm);
		cpSpaceStep(scene->space, frametime);
		scene->ExecuteFunctions(indexscene, this->vm);
		scene->ExecuteScheduleFunctions(indexscene, frametime, this->vm);
		for(int i = 0; i < scene->nlayers; i += 1){
			if (scene->layers[i] == NULL){
				continue;}
			layer = scene->layers[i];
			lua_pushvalue(vm, indexscene);
			refscene = luaL_ref(vm, LUA_REGISTRYINDEX);
			bange::BuildProxy(refscene, static_cast<bange::base *>(layer), vm);
			indexlayer = lua_gettop(vm);
			layer->ExecuteFunctions(indexlayer, this->vm);
			layer->ExecuteScheduleFunctions(indexlayer, frametime, this->vm);
			layer->Process(indexlayer, frametime, vm);
			lua_pop(vm, 1);//Proxy
		}
		//Next
		lua_pop(this->vm, 1);
	}
	lua_pop(vm, 1);//Scenes table
}

void bange::box::ProcessViews(void){
	int indexview = 0, refview = LUA_REFNIL;
	bange::baseproxy *proxy = NULL;
	bange::view *view = NULL;
	bange::scene *scene = NULL;
	lua_getfield(vm, 2, "views");
	if (!lua_istable(vm, -1)){
		lua_pop(vm, 1);
		return;}
	float frametime = this->GetFrameTime();
	lua_pushnil(this->vm);
	while(lua_next(this->vm, -2)){
		//Continue
		proxy = static_cast<bange::baseproxy *>(lua_touserdata(this->vm, -1));
		view = dynamic_cast<bange::view *>(proxy->base);
		if (view == NULL){
			cout << "bange: object to be processed isn't a view!" << endl;
			lua_pop(this->vm, 1);
			continue;}
		indexview = lua_gettop(vm);
		view->ExecuteFunctions(indexview, this->vm);
		view->ExecuteScheduleFunctions(indexview, frametime, this->vm);
		if (view->refscene != LUA_REFNIL){
			lua_rawgeti(vm, LUA_REGISTRYINDEX, view->refscene);
			bange::base *base = bange::base::GetBaseFromProxy(-1, vm);
			bange::scene *scene = static_cast<bange::scene *>(base);		
			this->SetView(*view->theview);
			for(int i = 0; i < scene->nlayers; i += 1){
				if (scene->layers[i] == NULL || scene->layers[i]->visible == false){
					continue;}
				scene->layers[i]->Draw(this->renderwindow, *view->theview);
			}
			lua_pop(vm, 1);//proxy
		}
		//Next
		lua_pop(this->vm, 1);
	}
	lua_pop(vm, 1);//Views table
}

/*
void bangebox_Process(sfRenderWindow *mainwindow, lua_State *vm){
    bange_view *view = NULL;
    bange_scene *scene = NULL;
    bange_layer *layer = NULL;
    sfFloatRect rect = {0.0};
    float difx = 0.0, dify = 0.0, fixx = 0.0, fixy = 0.0;
    int ilayer = 0, indexobject = 0;
    lua_pushnil(vm);
    while(lua_next(vm, -2)){
        lua_getmetatable(vm, -1);
        lua_getfield(vm, LUA_REGISTRYINDEX, "bange_scene");
        if(!lua_equal(vm, -1, -2)){
            lua_pop(vm, 3);//The two metatables and the Next
            continue;}
        lua_pop(vm, 2);
        scene = lua_touserdata(vm, -1);
        if (scene->velocity == 1.0){
			cpSpaceStep(scene->space, sfRenderWindow_GetFrameTime(mainwindow));}
		else if (scene->velocity >= 0.0){
			cpSpaceStep(scene->space, sfRenderWindow_GetFrameTime(mainwindow) * scene->velocity);}
        for(ilayer = 0; ilayer < scene->nlayers; ilayer += 1){
            layer = &scene->layer[ilayer];
            bangelayerproxy_GetProxy(layer, vm);
            indexobject = lua_gettop(vm);
            bangefunction_Process(layer->functions, indexobject, layer->data, vm);
            bangeschedulefunction_Process(layer->schedulefunctions, indexobject, layer->data, sfRenderWindow_GetFrameTime(mainwindow), vm);
            lua_pop(vm, 1);//Layer proxy
            if(layer->process != NULL){
                layer->process(layer, vm);}
        }
        lua_rawgeti(vm, LUA_REGISTRYINDEX, scene->views);
        lua_pushnil(vm);
        while(lua_next(vm, -2)){
            view = lua_touserdata(vm, -1);
            rect = sfView_GetRect(view->view);
			difx = view->newx - rect.Left;
			dify = view->newy - rect.Top;
            sfView_Move(view->view, difx / view->smoothness, dify / view->smoothness);
            //Fix camera
            rect = sfView_GetRect(view->view);
            if (rect.Right > scene->width){
				fixx = -(rect.Right - scene->width);
				view->newx = rect.Left;}
			else if (rect.Left < 0.0){
				fixx = -rect.Left;
				view->newx = 0.0;}
			if (rect.Bottom > scene->height){
				fixy = -(rect.Bottom - scene->height);
				view->newy = rect.Top;}
			else if (rect.Top < 0.0){
				fixy = -rect.Top;
				view->newy = 0.0;}
			sfView_Move(view->view, fixx, fixy);
            //---
            view->lastrect = sfView_GetRect(view->view);
            sfRenderWindow_SetView(mainwindow, view->view);
            for(ilayer = 0; ilayer < scene->nlayers; ilayer += 1){
                layer = &scene->layer[ilayer];
                if (layer->type != LayerNone && layer->visible){
                    layer->draw(layer, scene, view, mainwindow, vm);}
            }
            //next
            lua_pop(vm, 1);
        }
        lua_pop(vm, 1);//Pop scene->views
        //Next
        lua_pop(vm, 1);
    }
}
*/

void bange::box::SetExitFunction(int function, int data){
	luaL_unref(this->vm, LUA_REGISTRYINDEX, this->callexitfunction);
	luaL_unref(this->vm, LUA_REGISTRYINDEX, this->dataexitfunction);
	this->callexitfunction = function;
	this->dataexitfunction = data;
}

void bange::box::CallExitFunction(void){
	if (this->callexitfunction == LUA_REFNIL){
        return;}
    lua_rawgeti(this->vm, LUA_REGISTRYINDEX, this->callexitfunction);
    lua_rawgeti(this->vm, LUA_REGISTRYINDEX, this->dataexitfunction);
    lua_call(this->vm, 1, 0);
}

void bange::box::SetBgColor(int r, int g, int b){
	sf::Color &color = this->bgcolor;
	color.r = r;
	color.g = g;
	color.b = b;
}

bange::box::~box(){
	if (this->vm != NULL){
		lua_close(this->vm);}
	//delete this->defaultview;
	std::cout << "Destroying box" << std::endl;
}

PHYSFS_sint64 bange::readfilefrompackage(const char *filename, char **pbuffer){
	PHYSFS_file *file = NULL;
	PHYSFS_sint64 filelength = 0, readed = 0;
	if((file = PHYSFS_openRead(filename)) == NULL){
			cout << "bange: Error opening \"" << filename << "\": " << PHYSFS_getLastError() << endl;
		return -1;
	}
	filelength = PHYSFS_fileLength(file);
	char *buffer = new (nothrow) char[filelength+1];
	readed = PHYSFS_read(file, static_cast<void *>(buffer), filelength, 1);
	if(readed != 1){
		cout << "bange: Error reading \"" << filename << "\": " << PHYSFS_getLastError() << endl;}
	PHYSFS_close(file);
	buffer[filelength] = '\0';
	*pbuffer = buffer;
	return readed;
}

PHYSFS_sint64 bange::readbinaryfrompackage(const char *filename, char **pbuffer){
	PHYSFS_file *file = NULL;
	PHYSFS_sint64 filelength = 0, readed = 0;
	if((file = PHYSFS_openRead(filename)) == NULL){
			cout << "bange: Error opening \"" << filename << "\": " << PHYSFS_getLastError() << endl;
		return -1;
	}
	filelength = PHYSFS_fileLength(file);
	char *buffer = new (nothrow) char[filelength];
	readed = PHYSFS_read(file, static_cast<void *>(buffer), 1, filelength);
	if(readed < 1){
		cout << "bange: Error reading \"" << filename << "\": " << PHYSFS_getLastError() << endl;}
	PHYSFS_close(file);
	*pbuffer = buffer;
	return readed;
}

bange::box *bange::getbangebox(lua_State *vm){
	lua_getfield(vm, LUA_REGISTRYINDEX, "bangebox");
    bange::box *box = static_cast<bange::box *>(lua_touserdata(vm, -1));
    lua_pop(vm, 1);
    return box;
}

void bange::RegisterVM(lua_State *vm){
	luaL_reg functions[] = {
    {"SetExitFunction", bangebox_SetExitFunction},
    {"SetBgColor", bangebox_SetBgColor},
    {"SetVSync", bangebox_SetVSync},
    {"ShowCursor", bangebox_ShowCursor},
    {"SetKeyRepeat", bangebox_SetKeyRepeat},
    {"SetFPS", bangebox_SetFPS},
    {"GetTime", bangebox_GetTime},
    {NULL, NULL}};
    luaL_register(vm, NULL, functions);
}

static int bangebox_SetExitFunction(lua_State *vm){
	//function, data
	bange::box *box = bange::getbangebox(vm);
	int data = luaL_ref(vm, LUA_REGISTRYINDEX);
    int function = luaL_ref(vm, LUA_REGISTRYINDEX);
    box->SetExitFunction(function, data);
    return 0;
}

static int bangebox_SetBgColor(lua_State *vm){
    //r, g, b
    sf::Uint8 r = 0, g = 0, b = 0, a = 255;
    bange::box *box = bange::getbangebox(vm);
    if (lua_isnumber(vm, 1)){
        r = static_cast<sf::Uint8>(lua_tonumber(vm, 1));}
    if (lua_isnumber(vm, 2)){
        g = static_cast<sf::Uint8>(lua_tonumber(vm, 2));}
    if (lua_isnumber(vm, 3)){
        b = static_cast<sf::Uint8>(lua_tonumber(vm, 3));}
    if (lua_isnumber(vm, 4)){
        a = static_cast<sf::Uint8>(lua_tonumber(vm, 4));}
    box->SetBgColor(r, g, b);
    return 0;
}

static int bangebox_SetVSync(lua_State *vm){
	//bool
    if (lua_isnil(vm, 1) || !lua_isboolean(vm, 1)){
        return 0;}
    bange::box *box = bange::getbangebox(vm);
    printf("\nVSync: %d", lua_toboolean(vm, 1));
    box->UseVerticalSync(static_cast<bool>(lua_toboolean(vm, 1)));
    return 0;
}

static int bangebox_ShowCursor(lua_State *vm){
	//bool
    if (lua_isnil(vm, 1) || !lua_isboolean(vm, 1)){
        return 0;}
    bange::box *box = bange::getbangebox(vm);
    box->ShowMouseCursor(static_cast<bool>(lua_toboolean(vm, 1)));
    return 0;
}

static int bangebox_SetKeyRepeat(lua_State *vm){
	//bool
    if (lua_isnil(vm, 1) || !lua_isboolean(vm, 1)){
        return 0;}
    bange::box *box = bange::getbangebox(vm);
    box->EnableKeyRepeat(static_cast<bool>(lua_toboolean(vm, 1)));
    return 0;
}

static int bangebox_SetFPS(lua_State *vm){
    //fps
    if (lua_isnil(vm, 1) || !lua_isnumber(vm, 1)){
        return 0;}
    bange::box *box = bange::getbangebox(vm);
    box->SetFramerateLimit(static_cast<bool>(lua_tonumber(vm, 1)));
    return 0;
}

static int bangebox_GetTime(lua_State *vm){
	//without parameters
    bange::box *box = bange::getbangebox(vm);
    lua_pushnumber(vm, box->GetFrameTime());
    return 1;
}
